#pragma once
/* 
* COPYRIGHT 2012 Mafahir Fairoze
* 
* This file is part of Vector Template Library (Vtl) v1.0.
* (Project Website : http://mafahir.wordpress.com/projects/neuralplusplus)
* 
* Vector Template Library (Vtl) v1.0 is a free software. You can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
* 
* Vector Template Library (Vtl) v1.0 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License <http://www.gnu.org/licenses/> for more details.
*/

#include "Vtl/eigen.h"

namespace Vtl
{
	template <typename Scalar, int Dimension>
	class Triangle
	{
	public:
		typedef Eigen::Matrix<Scalar, Dimension, 1> VectorType;
		typedef Eigen::Hyperplane<Scalar, Dimension> Hyperplane;
		typedef Eigen::ParametrizedLine<Scalar, Dimension> ParametrizedLine;

		/*!
		*  \brief Default constructor.
		*/
		Triangle(void)
		{
		}

		/*!
		*  \brief Constructor.
		*  \param[in] _PointA 
		*  \param[in] _PointB 
		*  \param[in] _PointC 
		*/
		Triangle(const VectorType &_PointA, const VectorType &_PointB, const VectorType &_PointC)
			:pointA(_PointA), pointB(_PointB), pointC(_PointC)
		{
		}

		/*!
		*  \brief Checks if point is inside Triangle.
		*  \param[in] _Point 
		*  \param[in] _Eps (optional) Epsilon, increased value will result reduced accuracy of computation. Defaults to 1e-4.
		*  \return bool returns TRUE if it is inside.
		*/
		bool Contains(const VectorType &_Point, const double &_Eps = 1e-5)
		{
			bool is_inside = false;
			VectorType bminusa = pointC - pointB;
			is_inside = (bminusa.cross(_Point - pointB).dot(bminusa.cross(pointA - pointB)) >= _Eps);
			if(!is_inside) return false;

			bminusa = pointC - pointA;
			is_inside = (bminusa.cross(_Point - pointA).dot(bminusa.cross(pointB - pointA)) >= _Eps);
			if(!is_inside) return false;

			bminusa = pointB - pointA;
			is_inside = (bminusa.cross(_Point - pointA).dot(bminusa.cross(pointC - pointA)) >= _Eps);
			if(!is_inside) return false;

			return true;
		}

		/*!
		*  \brief Intersects line with triangle.
		*  \param[in] _Line 
		*  \param[out] _Point (optional) the intersection point. Defaults to NULL.
		*  \param[in] _Eps (optional) Epsilon, increased value will result reduced accuracy of computation. Defaults to 1e-4.
		*  \return bool returns TRUE if does intersect.
		*/
		bool IntersectWithLine(const ParametrizedLine &_Line, Eigen::Matrix<Scalar, Dimension, 1> *_Point = NULL, const double &_Eps = 1e-5)
		{
			Hyperplane plane = Hyperplane::Through(pointA, pointB, pointC);
			Scalar t = _Line.intersection(plane);
			VectorType point = (_Line.origin() + _Line.direction() * t);
			if(!Contains(point, _Eps))
				return false;

			if(_Point != NULL)
				*_Point = point;
			return true;
		}

	protected:
		VectorType pointA;
		VectorType pointB;
		VectorType pointC;
	};

	typedef Triangle<double, 3> Triangle3d;
	typedef Triangle<double, 2> Triangle2d;
	typedef Triangle<float, 3> Triangle3f;
	typedef Triangle<float, 2> Triangle2f;
	typedef Triangle<int, 3> Triangle3i;
	typedef Triangle<int, 2> Triangle2i;
};